1   /*
2    * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package sun.awt.X11;
26  
27  import java.awt.*;
28  import java.awt.event.*;
29  import java.awt.geom.Point2D;
30  import java.lang.ref.WeakReference;
31  import sun.java2d.SunGraphics2D;
32  import sun.java2d.pipe.Region;
33  import sun.awt.AWTAccessor;
34  import sun.awt.SunToolkit;
35  
36  class XWarningWindow extends XWindow {
37      private final static int SHOWING_DELAY = 330;
38      private final static int HIDING_DELAY = 2000;
39  
40      private final Window ownerWindow;
41      private WeakReference<XWindowPeer> ownerPeer;
42      private long parentWindow;
43  
44      private final static String OWNER = "OWNER";
45      private InfoWindow.Tooltip tooltip;
46  
47      /**
48       * Animation stage.
49       */
50      private volatile int currentIcon = 0;
51  
52      /* -1 - uninitialized.
53       * 0 - 16x16
54       * 1 - 24x24
55       * 2 - 32x32
56       * 3 - 48x48
57       */
58      private int currentSize = -1;
59      private static XIconInfo[][] icons;
60      private static XIconInfo getSecurityIconInfo(int size, int num) {
61          synchronized (XWarningWindow.class) {
62              if (icons == null) {
63                  icons = new XIconInfo[4][3];
64                  if (XlibWrapper.dataModel == 32) {
65                      icons[0][0] = new XIconInfo(XAWTIcon32_security_icon_bw16_png.security_icon_bw16_png);
66                      icons[0][1] = new XIconInfo(XAWTIcon32_security_icon_interim16_png.security_icon_interim16_png);
67                      icons[0][2] = new XIconInfo(XAWTIcon32_security_icon_yellow16_png.security_icon_yellow16_png);
68                      icons[1][0] = new XIconInfo(XAWTIcon32_security_icon_bw24_png.security_icon_bw24_png);
69                      icons[1][1] = new XIconInfo(XAWTIcon32_security_icon_interim24_png.security_icon_interim24_png);
70                      icons[1][2] = new XIconInfo(XAWTIcon32_security_icon_yellow24_png.security_icon_yellow24_png);
71                      icons[2][0] = new XIconInfo(XAWTIcon32_security_icon_bw32_png.security_icon_bw32_png);
72                      icons[2][1] = new XIconInfo(XAWTIcon32_security_icon_interim32_png.security_icon_interim32_png);
73                      icons[2][2] = new XIconInfo(XAWTIcon32_security_icon_yellow32_png.security_icon_yellow32_png);
74                      icons[3][0] = new XIconInfo(XAWTIcon32_security_icon_bw48_png.security_icon_bw48_png);
75                      icons[3][1] = new XIconInfo(XAWTIcon32_security_icon_interim48_png.security_icon_interim48_png);
76                      icons[3][2] = new XIconInfo(XAWTIcon32_security_icon_yellow48_png.security_icon_yellow48_png);
77                  } else {
78                      icons[0][0] = new XIconInfo(XAWTIcon64_security_icon_bw16_png.security_icon_bw16_png);
79                      icons[0][1] = new XIconInfo(XAWTIcon64_security_icon_interim16_png.security_icon_interim16_png);
80                      icons[0][2] = new XIconInfo(XAWTIcon64_security_icon_yellow16_png.security_icon_yellow16_png);
81                      icons[1][0] = new XIconInfo(XAWTIcon64_security_icon_bw24_png.security_icon_bw24_png);
82                      icons[1][1] = new XIconInfo(XAWTIcon64_security_icon_interim24_png.security_icon_interim24_png);
83                      icons[1][2] = new XIconInfo(XAWTIcon64_security_icon_yellow24_png.security_icon_yellow24_png);
84                      icons[2][0] = new XIconInfo(XAWTIcon64_security_icon_bw32_png.security_icon_bw32_png);
85                      icons[2][1] = new XIconInfo(XAWTIcon64_security_icon_interim32_png.security_icon_interim32_png);
86                      icons[2][2] = new XIconInfo(XAWTIcon64_security_icon_yellow32_png.security_icon_yellow32_png);
87                      icons[3][0] = new XIconInfo(XAWTIcon64_security_icon_bw48_png.security_icon_bw48_png);
88                      icons[3][1] = new XIconInfo(XAWTIcon64_security_icon_interim48_png.security_icon_interim48_png);
89                      icons[3][2] = new XIconInfo(XAWTIcon64_security_icon_yellow48_png.security_icon_yellow48_png);
90                  }
91              }
92          }
93          final int sizeIndex = size % icons.length;
94          return icons[sizeIndex][num % icons[sizeIndex].length];
95      }
96  
97      private void updateIconSize() {
98          int newSize = -1;
99  
100         if (ownerWindow != null) {
101             Insets insets = ownerWindow.getInsets();
102             int max = Math.max(insets.top, Math.max(insets.bottom,
103                         Math.max(insets.left, insets.right)));
104             if (max < 24) {
105                 newSize = 0;
106             } else if (max < 32) {
107                 newSize = 1;
108             } else if (max < 48) {
109                 newSize = 2;
110             } else {
111                 newSize = 3;
112             }
113         }
114         // Make sure we have a valid size
115         if (newSize == -1) {
116             newSize = 0;
117         }
118 
119         // Note: this is not the most wise solution to use awtLock here,
120         // this should have been sync'ed with the stateLock. However,
121         // the awtLock must be taken first (see XBaseWindow.getStateLock()),
122         // and we need the awtLock anyway to update the shape of the icon.
123         // So it's easier to use just one lock instead.
124         XToolkit.awtLock();
125         try {
126             if (newSize != currentSize) {
127                 currentSize = newSize;
128                 XIconInfo ico = getSecurityIconInfo(currentSize, 0);
129                 XlibWrapper.SetBitmapShape(XToolkit.getDisplay(), getWindow(),
130                         ico.getWidth(), ico.getHeight(), ico.getIntData());
131                 AWTAccessor.getWindowAccessor().setSecurityWarningSize(
132                         ownerWindow, ico.getWidth(), ico.getHeight());
133             }
134         } finally {
135             XToolkit.awtUnlock();
136         }
137     }
138 
139     private XIconInfo getSecurityIconInfo() {
140         updateIconSize();
141         return getSecurityIconInfo(currentSize, currentIcon);
142     }
143 
144     XWarningWindow(final Window ownerWindow, long parentWindow, XWindowPeer ownerPeer) {
145         super(new XCreateWindowParams(new Object[] {
146                         TARGET, ownerWindow,
147                         OWNER, Long.valueOf(parentWindow)
148         }));
149         this.ownerWindow = ownerWindow;
150         this.parentWindow = parentWindow;
151         this.tooltip = new InfoWindow.Tooltip(null, getTarget(),
152                 new InfoWindow.Tooltip.LiveArguments() {
153                     public boolean isDisposed() {
154                         return XWarningWindow.this.isDisposed();
155                     }
156                     public Rectangle getBounds() {
157                         return XWarningWindow.this.getBounds();
158                     }
159                     public String getTooltipString() {
160                         return XWarningWindow.this.ownerWindow.getWarningString();
161                     }
162                 });
163         this.ownerPeer = new WeakReference<XWindowPeer>(ownerPeer);
164     }
165 
166     private void requestNoTaskbar() {
167         XNETProtocol netProtocol = XWM.getWM().getNETProtocol();
168         if (netProtocol != null) {
169             netProtocol.requestState(this, netProtocol.XA_NET_WM_STATE_SKIP_TASKBAR, true);
170         }
171     }
172 
173     @Override
174     void postInit(XCreateWindowParams params) {
175         super.postInit(params);
176         XToolkit.awtLock();
177         try {
178             XWM.setMotifDecor(this, false, 0, 0);
179             XWM.setOLDecor(this, false, 0);
180 
181             long parentWindow = ((Long)params.get(OWNER)).longValue();
182             XlibWrapper.XSetTransientFor(XToolkit.getDisplay(),
183                     getWindow(), parentWindow);
184 
185             XWMHints hints = getWMHints();
186             hints.set_flags(hints.get_flags() | (int)XUtilConstants.InputHint | (int)XUtilConstants.StateHint);
187             hints.set_input(false);
188             hints.set_initial_state(XUtilConstants.NormalState);
189             XlibWrapper.XSetWMHints(XToolkit.getDisplay(), getWindow(), hints.pData);
190 
191             initWMProtocols();
192             requestNoTaskbar();
193         } finally {
194             XToolkit.awtUnlock();
195         }
196     }
197 
198     /**
199      * @param x,y,w,h coordinates of the untrusted window
200      */
201     public void reposition(int x, int y, int w, int h) {
202         Point2D point = AWTAccessor.getWindowAccessor().
203             calculateSecurityWarningPosition(ownerWindow,
204                 x, y, w, h);
205         reshape((int)point.getX(), (int)point.getY(), getWidth(), getHeight());
206     }
207 
208     protected String getWMName() {
209         return "Warning window";
210     }
211 
212     public Graphics getGraphics() {
213         if ((surfaceData == null) || (ownerWindow == null)) return null;
214         return getGraphics(surfaceData,
215                                  getColor(),
216                                  getBackground(),
217                                  getFont());
218     }
219     void paint(Graphics g, int x, int y, int width, int height) {
220         g.drawImage(getSecurityIconInfo().getImage(), 0, 0, null);
221     }
222 
223     String getWarningString() {
224         return ownerWindow.getWarningString();
225     }
226 
227     int getWidth() {
228         return getSecurityIconInfo().getWidth();
229     }
230 
231     int getHeight() {
232         return getSecurityIconInfo().getHeight();
233     }
234 
235     Color getBackground() {
236         return SystemColor.window;
237     }
238     Color getColor() {
239         return Color.black;
240     }
241     Font getFont () {
242         return ownerWindow.getFont();
243     }
244     public void repaint() {
245         Rectangle bounds = getBounds();
246         Graphics g = getGraphics();
247         try {
248             paint(g, 0, 0, bounds.width, bounds.height);
249         } finally {
250             g.dispose();
251         }
252     }
253 
254     @Override
255     public void handleExposeEvent(XEvent xev) {
256         super.handleExposeEvent(xev);
257 
258         XExposeEvent xe = xev.get_xexpose();
259         final int x = xe.get_x();
260         final int y = xe.get_y();
261         final int width = xe.get_width();
262         final int height = xe.get_height();
263         SunToolkit.executeOnEventHandlerThread(target,
264                 new Runnable() {
265                     public void run() {
266                         Graphics g = getGraphics();
267                         try {
268                             paint(g, x, y, width, height);
269                         } finally {
270                             g.dispose();
271                         }
272                     }
273                 });
274     }
275 
276     @Override
277     protected boolean isEventDisabled(XEvent e) {
278         return true;
279     }
280 
281     /** Send a synthetic UnmapNotify in order to withdraw the window.
282      */
283     private void withdraw() {
284         XEvent req = new XEvent();
285         try {
286             long root;
287             XToolkit.awtLock();
288             try {
289                 root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber());
290             }
291             finally {
292                 XToolkit.awtUnlock();
293             }
294 
295             req.set_type(XConstants.UnmapNotify);
296 
297             XUnmapEvent umev = req.get_xunmap();
298 
299             umev.set_event(root);
300             umev.set_window(getWindow());
301             umev.set_from_configure(false);
302 
303             XToolkit.awtLock();
304             try {
305                 XlibWrapper.XSendEvent(XToolkit.getDisplay(),
306                         root,
307                         false,
308                         XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
309                         req.pData);
310             }
311             finally {
312                 XToolkit.awtUnlock();
313             }
314         } finally {
315             req.dispose();
316         }
317     }
318 
319     @Override
320     protected void stateChanged(long time, int oldState, int newState) {
321         if (newState == XUtilConstants.IconicState) {
322             super.xSetVisible(false);
323             withdraw();
324         }
325     }
326 
327     @Override
328     protected void setMouseAbove(boolean above) {
329         super.setMouseAbove(above);
330         XWindowPeer p = ownerPeer.get();
331         if (p != null) {
332             p.updateSecurityWarningVisibility();
333         }
334     }
335 
336     @Override
337     protected void enterNotify(long window) {
338         super.enterNotify(window);
339         if (window == getWindow()) {
340             tooltip.enter();
341         }
342     }
343 
344     @Override
345     protected void leaveNotify(long window) {
346         super.leaveNotify(window);
347         if (window == getWindow()) {
348             tooltip.exit();
349         }
350     }
351 
352     @Override
353     public void xSetVisible(boolean visible) {
354         super.xSetVisible(visible);
355 
356         // The _NET_WM_STATE_SKIP_TASKBAR got reset upon hiding/showing,
357         // so we request it every time whenever we change the visibility.
358         requestNoTaskbar();
359     }
360 
361     private final Runnable hidingTask = new Runnable() {
362         public void run() {
363             xSetVisible(false);
364         }
365     };
366 
367     private final Runnable showingTask = new Runnable() {
368         public void run() {
369             if (!isVisible()) {
370                 xSetVisible(true);
371                 updateIconSize();
372                 XWindowPeer peer = ownerPeer.get();
373                 if (peer != null) {
374                     peer.repositionSecurityWarning();
375                 }
376             }
377             repaint();
378             if (currentIcon > 0) {
379                 currentIcon--;
380                 XToolkit.schedule(showingTask, SHOWING_DELAY);
381             }
382         }
383     };
384 
385     public void setSecurityWarningVisible(boolean visible, boolean doSchedule) {
386         if (visible) {
387             XToolkit.remove(hidingTask);
388             XToolkit.remove(showingTask);
389             if (isVisible()) {
390                 currentIcon = 0;
391             } else {
392                 currentIcon = 3;
393             }
394             if (doSchedule) {
395                 XToolkit.schedule(showingTask, 1);
396             } else {
397                 showingTask.run();
398             }
399         } else {
400             XToolkit.remove(showingTask);
401             XToolkit.remove(hidingTask);
402             if (!isVisible()) {
403                 return;
404             }
405             if (doSchedule) {
406                 XToolkit.schedule(hidingTask, HIDING_DELAY);
407             } else {
408                 hidingTask.run();
409             }
410         }
411     }
412 }